#include "MultiDnsResolver.hpp"

#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netdb.h>

#include <pthread.h>

#include <iostream>

using namespace std;

pthread_mutex_t  gMutex;
pthread_t*       gThreadArray;

MultiDnsResolver::MultiDnsResolver(int threadNum) : mThreadNum(threadNum)
{
    gThreadArray = new pthread_t[this->mThreadNum];
    pthread_mutex_init(&gMutex, NULL);
}

MultiDnsResolver::~MultiDnsResolver()
{
    delete [] gThreadArray;
    pthread_mutex_destroy(&gMutex);
}

string
MultiDnsResolver::GetIP(const string& crDomainName)
{
    struct addrinfo  hints;
    struct addrinfo* res = NULL;

    memset(&hints, 0, sizeof(hints));
    hints.ai_family   = AF_UNSPEC;
    hints.ai_socktype = SOCK_STREAM;
    
    string ip_address;

    int ret = getaddrinfo(crDomainName.c_str(), NULL, &hints, &res);
    if (ret == 0 && res != NULL) {
        char buff[INET_ADDRSTRLEN];
        If (getnameinfo(res->ai_addr, res->ai_addrlen, buff, INET_ADDRSTRLEN,
                        NULL, 0, NI_NUMERICHOST) == 0)
            ip_address = Buff;
        
        freeaddrinfo (res);
    }
    return ip_address;
}



int
MultiDnsResolver::GetIPList(map<string, string>& rDomainIpMap)
{
    mJobPool.clear();
    mJobPool.reserve(mThreadNum);
    
    int counter = 0;
    map<string, string>::iterator it;
    for (it=rDomainIpMap.begin(); it!=rDomainIpMap.end(); ++it) {
        mJobPool[counter%mThreadNum].push_back(it);
        ++counter;
    }

    // for debug
    //     for (unsigned i=0; i<mThreadNum; ++i) {
    //         for (unsigned j=0; j<mJobPool[i].size(); ++j) {
    //             cout << mJobPool[i][j]->first << "\t";
    //         }
    //         cout << endl;
    //     }
    
    int err = 0;
    
    for (int i=0; i<mThreadNum; ++i) {
        ThreadArg thread_arg;
        thread_arg.thd = this;
        thread_arg.idx = i;
        err = pthread_create(&gThreadArray[i], NULL, StartThread, &thread_arg);
        // @TODO: need exception and log
        if (err == -1) {
            cerr << "Error in creating thread!" << endl;
            break;
        }
    }

    for (int i=0; i<mThreadNum; ++i) {
        pthread_join(gThreadArray[i], NULL);
    }

    return err;
}

void *
MultiDnsResolver::StartThread(void* arg)
{
    ThreadArg* thread_arg = static_cast<ThreadArg* >(arg);
    thread_arg->thd->ThreadResolve(thread_arg->idx);
    return (static_cast<void *>(0));
}

void
MultiDnsResolver::ThreadResolve(int idx)
{
    map<string, string>::iterator it;
    // cout << mJobPool[idx].size() << endl; 
    for (unsigned i=0; i<mJobPool[idx].size(); ++i) {
        it = mJobPool[idx][i];
        it->second = this->GetIP(it->first);
    }
}
